<?php
///**
// * Created by PhpStorm.
// * User: Qch
// * Date: 2016/8/13
// * Time: 17:24
// */
//
//namespace Aoe\Bootstrap;
//
//
//use Closure;
//
///**
// *
// * # 标准自动加载器 [PSR-4]
// *
// */
//class Loader
//{
//    const KEY_NAMESPACE     = 'namespace';
//    const FILE_NAME_EMPTY   = '文件名为空';
//    const CLASS_NAME_ERROR  = '类名${classname}错误';
//    const CLASS_NOT_DEFINED = '${file}中没有找到${classname}类定义';
//    const CLASS_RELOAD      = '${classname}类重复加载';
//    const FILE_NOT_FINED    = '${classname}关联文件未找到';
//
//    /**
//     * 已经加载的类，缓存数据
//     * 类名到文件的映射
//     *
//     * @var array
//     *
//     */
//    protected array $loaded_class    = [];
//
//    /**
//     * 域名空间到文件目录的映射
//     *
//     * @var array
//     *
//     */
//    protected array $namespace_paths  = [];
//
//    /**
//     * 类名到文件的映射，预定义数据
//     *
//     * @var array
//     *
//     */
//    protected array $class_files      = [];
//
//    /**
//     * 查找类定义文件过程日志，临时数据
//     *
//     * @var array
//     *
//     */
//    protected array $tried_files      = [];
//
//    /**
//     * 错误信息钩子
//     *
//     * @var (closure(string, array):void) | null
//     *
//     */
//    protected ?Closure $error_handler = null;
//
//    /**
//     * ## 设定错误处理函数
//     *
//     * @param closure(string, array):void $handler
//     */
//    public function setErrorHandler(Closure $handler): void
//    {
//        $this->error_handler = $handler;
//    }
//
//    /**
//     * ## 添加类命名空间前缀路径
//     *
//     * @param string $prefix 类命名空间前缀
//     *
//     * @param string $paths  类定义文件所在目录
//     *
//     * @return void
//     */
//    public function addNamespace(string $prefix, string $paths): void
//    {
//        $prefix = trim($prefix, '\\');
//        if (isset($this->namespace_paths[$prefix]))
//            $this->namespace_paths[$prefix] = [];
//        foreach ((array)$paths as $path) {
//            $path                             = rtrim($path, DIRECTORY_SEPARATOR);
//            $this->namespace_paths[$prefix][] = $path;
//        }
//    }
//
//    /**
//     * ## Registers this autoloader with SPL.
//     *
//     * @param bool $prepend
//     *
//     * @return void
//     *
//     */
//    public function register(bool $prepend = false): void
//    {
//        spl_autoload_register([$this, 'loadClassByName'], true, $prepend);
//    }
//
//    /**
//     * Unregisters this autoloader from SPL.
//     */
//    public function __destruct()
//    {
//        spl_autoload_unregister([$this, 'loadClassByName']);
//    }
//
//    /**
//     * 获取所有的命名空间前缀映射
//     *
//     * @return array
//     *
//     */
////    public function getNamespacePaths()
////    {
////        return $this->namespace_paths;
////    }
//
//    /**
//     * 设定特定类的定义文件
//     *
//     * @param string $class 类名
//     *
//     * @param string $file  类文件
//     *
//     * @return void
//     *
//     */
////    public function setClass($class, $file)
////    {
////        $spec = trim($class, '\\');
////        $this->class_files[$spec] = $file;
////    }
//
//    /**
//     * 获取所有解析的类定义文件
//     *
//     * @return array
//     *
//     */
////    public function getClassFiles()
////    {
////        return $this->class_files;
////    }
//
//    /**
//     * 获取所有加载完成的类
//     *
//     * @return array 类名到类定义文件的映射
//     *
//     */
////    public function getLoadedClass()
////    {
////        return $this->loaded_class;
////    }
//
//    /**
//     * ## 根据类名加载类定义文件
//     *
//     * @param string $classname 类名
//     */
//    protected function loadClassByName(string $classname): void
//    {
//        if (!is_classname($classname)) {
//            $this->onError(self::CLASS_NAME_ERROR, compact('classname'));
//            return;
//        }
//
//        // is the class already loaded?
//        if ($this->isDeclared($classname)) {
//            $this->onError(self::CLASS_RELOAD, compact('classname'));
//            return;
//        }
//
//        // find the class file
//        $file = $this->findTheDefinitionFileOf($classname);
//        if (!$file) {
//            $this->onError(self::FILE_NOT_FINED, compact('classname'));
//            return;
//        }
//
//        // load the file
//        require $file;
//
//        // declared now?
//        if (!$this->isDeclared($classname)) {
//            $this->onError(self::CLASS_NOT_DEFINED, compact('classname', 'file'));
//            return;
//        }
//
//        // done!
//        $this->loaded_class[$classname] = $file;
//    }
//
//    /**
//     * 错误处理
//     *
//     * @param string $error   错误码
//     * @param array  $context 参数
//     */
//    protected function onError(string $error, array $context = []): void
//    {
//        if ($this->error_handler) ($this->error_handler)($error, $context);
//    }
//
//    /**
//     * 检测类是否已加载
//     *
//     * @param string $spec The class or interface.
//     *
//     * @return bool
//     */
//    protected function isDeclared(string $spec): bool
//    {
//        return class_exists($spec, false)
//               || interface_exists($spec, false)
//               || trait_exists($spec, false);
//    }
//
//    /**
//     * ## 查找类定义文件
//     *
//     * @param string $spec 类名
//     *
//     * @return bool|string 文件名
//     */
//    protected function findTheDefinitionFileOf(string $spec): bool | string
//    {
//        $spec = trim($spec, '\\');
//
//        // does the class exist in the explicit class map?
//        if (isset($this->class_files[$spec]))
//            return $this->class_files[$spec];
//
//        $this->tried_files = [];
//
//        // convert class name to file name ...
//        // 请不要在代码中修改$classFilename的值
//        $classFilename = $this->classnameToFile($spec);
//        if ($classFilename === false) return false;
//
//        // 各种长度的命名空间定义均支持
//        foreach ($this->namespace_paths as $prefix => $paths) {
//
//            // get the length of the prefix
//            $len = strlen($prefix);
//
//            // does the prefix match?
//            if (substr($spec, 0, $len) != $prefix) continue;
//
//            // ... and go through each of the paths for the prefix
//            foreach ($paths as $path) {
//
//                // convert the remaining spec to a file name
//                $file = $path . DIRECTORY_SEPARATOR . $classFilename;
//
//                // track which paths we have tried
//                $this->tried_files[] = $file;
//
//                // does it exist?
//                if (is_readable($file)) {
//                    // found it; retain in class map
//                    return $file;
//                }
//            }
//        }
//
//        // 默认在VENDOR目录查找
//        $file = __VENDOR__ . DIRECTORY_SEPARATOR . $classFilename;
//        if (is_readable($file)) return $file;
//        return false;
//    }
//
//    /**
//     * ## PSR-4 模式解析文件名.
//     *
//     * @param string $spec 类名
//     *
//     * @return bool|string 文件名
//     */
//    protected function classnameToFile(string $spec): bool | string
//    {
//        if (!separate_classname($spec, $class, $namespace)) {
//            $this->onError(self::FILE_NAME_EMPTY);
//            return false;
//        }
//
//        $namespace = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
//        // convert class underscores
//        // done!
//        return $namespace
//               . str_replace('_', DIRECTORY_SEPARATOR, $class)
//               . '.php';
//    }
//}
